package com.example.guesswho.util

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.Log
import jp.co.cyberagent.android.gpuimage.GPUImage
import jp.co.cyberagent.android.gpuimage.filter.GPUImageCartoonFilter
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilterGroup
import jp.co.cyberagent.android.gpuimage.filter.GPUImageRGBFilter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.util.UUID

/**
 * CartoonFilter utility class for applying cartoon effects to images
 * Wraps GPUImage library and provides one-click cartoon filtering
 */
class CartoonFilter private constructor(private val context: Context) {
    
    private val scope = CoroutineScope(Dispatchers.IO + Job())
    private val gpuImage = GPUImage(context)
    
    // Predefined filter combinations
    enum class FilterStyle {
        CLASSIC_CARTOON,
        VIBRANT_CARTOON,
        SOFT_CARTOON,
        COMIC_BOOK,
        ANIME_STYLE
    }
    
    companion object {
        @Volatile
        private var instance: CartoonFilter? = null
        
        fun getInstance(context: Context): CartoonFilter {
            return instance ?: synchronized(this) {
                instance ?: CartoonFilter(context.applicationContext).also { instance = it }
            }
        }
        
        private const val TAG = "CartoonFilter"
        private const val MAX_IMAGE_SIZE = 2048 // Maximum dimension to prevent OOM
    }
    
    /**
     * Apply one-click cartoon filter to a bitmap
     * @param inputBitmap Source bitmap
     * @param style Filter style (default: CLASSIC_CARTOON)
     * @param callback Callback with result bitmap or error
     */
    fun applyCartoonFilter(
        inputBitmap: Bitmap,
        style: FilterStyle = FilterStyle.CLASSIC_CARTOON,
        callback: (Result<Bitmap>) -> Unit
    ) {
        scope.launch {
            try {
                val resultBitmap = processBitmap(inputBitmap, style)
                withContext(Dispatchers.Main) {
                    callback(Result.success(resultBitmap))
                }
            } catch (e: Exception) {
                Log.e(TAG, "Error applying cartoon filter", e)
                withContext(Dispatchers.Main) {
                    callback(Result.failure(e))
                }
            }
        }
    }
    
    /**
     * Apply cartoon filter to image from URI
     * @param imageUri Image URI
     * @param style Filter style
     * @param callback Callback with result bitmap or error
     */
    fun applyCartoonFilter(
        imageUri: Uri,
        style: FilterStyle = FilterStyle.CLASSIC_CARTOON,
        callback: (Result<Bitmap>) -> Unit
    ) {
        scope.launch {
            try {
                val inputStream = context.contentResolver.openInputStream(imageUri)
                if (inputStream != null) {
                    val originalBitmap = BitmapFactory.decodeStream(inputStream)
                    inputStream.close()
                    
                    if (originalBitmap != null) {
                        val resultBitmap = processBitmap(originalBitmap, style)
                        withContext(Dispatchers.Main) {
                            callback(Result.success(resultBitmap))
                        }
                    } else {
                        callback(Result.failure(Exception("Failed to decode bitmap from URI")))
                    }
                } else {
                    callback(Result.failure(Exception("Failed to open input stream")))
                }
            } catch (e: Exception) {
                Log.e(TAG, "Error processing image from URI", e)
                withContext(Dispatchers.Main) {
                    callback(Result.failure(e))
                }
            }
        }
    }
    
    /**
     * Apply cartoon filter and upload to Parse, returning CDN URL
     * @param inputBitmap Source bitmap
     * @param style Filter style
     * @param fileName Optional file name (random if not provided)
     * @param callback Callback with CDN URL or error
     */
    fun applyAndUploadCartoonFilter(
        inputBitmap: Bitmap,
        style: FilterStyle = FilterStyle.CLASSIC_CARTOON,
        fileName: String? = null,
        callback: (Result<String>) -> Unit
    ) {
        scope.launch {
            try {
                // Step 1: Apply filter
                val filteredBitmap = processBitmap(inputBitmap, style)
                
                // Step 2: Convert to byte array
                val byteArray = bitmapToByteArray(filteredBitmap)
                
                // Step 3: Upload to Parse
                val parseFile = com.parse.ParseFile(
                    fileName ?: "cartoon_${UUID.randomUUID()}.jpg",
                    byteArray
                )
                
                parseFile.saveInBackground { exception ->
                    if (exception == null) {
                        val cdnUrl = parseFile.url
                        scope.launch {
                            withContext(Dispatchers.Main) {
                                callback(Result.success(cdnUrl))
                            }
                        }
                    } else {
                        scope.launch {
                            withContext(Dispatchers.Main) {
                                callback(Result.failure(exception))
                            }
                        }
                    }
                }
                
            } catch (e: Exception) {
                Log.e(TAG, "Error in applyAndUploadCartoonFilter", e)
                withContext(Dispatchers.Main) {
                    callback(Result.failure(e))
                }
            }
        }
    }
    
    /**
     * Process bitmap with cartoon filter
     */
    private suspend fun processBitmap(inputBitmap: Bitmap, style: FilterStyle): Bitmap {
        return withContext(Dispatchers.Default) {
            // Resize bitmap if too large to prevent OOM
            val resizedBitmap = resizeBitmapIfNeeded(inputBitmap)
            
            // Apply GPUImage filter
            gpuImage.setImage(resizedBitmap)
            
            val filter = createFilterForStyle(style)
            gpuImage.setFilter(filter)
            
            val filteredBitmap = gpuImage.bitmapWithFilterApplied
            
            // Clean up
            if (resizedBitmap != inputBitmap) {
                resizedBitmap.recycle()
            }
            
            filteredBitmap
        }
    }
    
    /**
     * Create filter group for different cartoon styles
     */
    private fun createFilterForStyle(style: FilterStyle): GPUImageFilter {
        return when (style) {
            FilterStyle.CLASSIC_CARTOON -> {
                val cartoonFilter = GPUImageCartoonFilter()
                cartoonFilter.setThreshold(0.2f)
                cartoonFilter.setQuantizationLevels(6.0f)
                cartoonFilter
            }
            
            FilterStyle.VIBRANT_CARTOON -> {
                val filterGroup = GPUImageFilterGroup()
                filterGroup.addFilter(GPUImageRGBFilter())
                val cartoonFilter = GPUImageCartoonFilter()
                cartoonFilter.setThreshold(0.3f)
                cartoonFilter.setQuantizationLevels(8.0f)
                filterGroup.addFilter(cartoonFilter)
                filterGroup
            }
            
            FilterStyle.SOFT_CARTOON -> {
                val cartoonFilter = GPUImageCartoonFilter()
                cartoonFilter.setThreshold(0.1f)
                cartoonFilter.setQuantizationLevels(4.0f)
                cartoonFilter
            }
            
            FilterStyle.COMIC_BOOK -> {
                val cartoonFilter = GPUImageCartoonFilter()
                cartoonFilter.setThreshold(0.4f)
                cartoonFilter.setQuantizationLevels(10.0f)
                cartoonFilter
            }
            
            FilterStyle.ANIME_STYLE -> {
                val cartoonFilter = GPUImageCartoonFilter()
                cartoonFilter.setThreshold(0.15f)
                cartoonFilter.setQuantizationLevels(5.0f)
                cartoonFilter
            }
        }
    }
    
    /**
     * Resize bitmap if it exceeds maximum dimensions
     */
    private fun resizeBitmapIfNeeded(bitmap: Bitmap): Bitmap {
        val width = bitmap.width
        val height = bitmap.height
        
        if (width <= MAX_IMAGE_SIZE && height <= MAX_IMAGE_SIZE) {
            return bitmap
        }
        
        val ratio = MAX_IMAGE_SIZE.toFloat() / maxOf(width, height)
        val newWidth = (width * ratio).toInt()
        val newHeight = (height * ratio).toInt()
        
        return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true)
    }
    
    /**
     * Convert bitmap to byte array
     */
    private fun bitmapToByteArray(bitmap: Bitmap): ByteArray {
        val stream = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream)
        val byteArray = stream.toByteArray()
        stream.close()
        return byteArray
    }
    
    /**
     * Save bitmap to file
     */
    suspend fun saveBitmapToFile(
        bitmap: Bitmap,
        fileName: String,
        callback: (Result<File>) -> Unit
    ) {
        scope.launch {
            try {
                val file = File(context.cacheDir, fileName)
                val outputStream = FileOutputStream(file)
                bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
                outputStream.close()
                
                withContext(Dispatchers.Main) {
                    callback(Result.success(file))
                }
            } catch (e: Exception) {
                Log.e(TAG, "Error saving bitmap to file", e)
                withContext(Dispatchers.Main) {
                    callback(Result.failure(e))
                }
            }
        }
    }
    
    /**
     * Get available filter styles
     */
    fun getAvailableStyles(): List<FilterStyleInfo> {
        return listOf(
            FilterStyleInfo(
                FilterStyle.CLASSIC_CARTOON,
                "经典卡通",
                "传统的卡通效果，适合大多数场景"
            ),
            FilterStyleInfo(
                FilterStyle.VIBRANT_CARTOON,
                "鲜艳卡通", 
                "色彩鲜艳的卡通效果，更加生动"
            ),
            FilterStyleInfo(
                FilterStyle.SOFT_CARTOON,
                "柔和卡通",
                "柔和的卡通效果，适合温馨场景"
            ),
            FilterStyleInfo(
                FilterStyle.COMIC_BOOK,
                "漫画风格",
                "漫画书风格，对比度较高"
            ),
            FilterStyleInfo(
                FilterStyle.ANIME_STYLE,
                "动漫风格",
                "日式动漫风格效果"
            )
        )
    }
    
    /**
     * Clean up resources
     */
    fun release() {
        gpuImage.deleteImage()
        instance = null
    }
    
    /**
     * Data class for filter style information
     */
    data class FilterStyleInfo(
        val style: FilterStyle,
        val displayName: String,
        val description: String
    )
}

/**
 * Extension function to simplify cartoon filter application
 */
suspend fun Bitmap.applyCartoonFilter(
    context: Context,
    style: CartoonFilter.FilterStyle = CartoonFilter.FilterStyle.CLASSIC_CARTOON
): Result<Bitmap> {
    return suspendCancellableCoroutine { continuation ->
        CartoonFilter.getInstance(context).applyCartoonFilter(
            this,
            style
        ) { result ->
            continuation.resume(result)
        }
    }
}

/**
 * Extension function to apply filter and upload to Parse
 */
suspend fun Bitmap.applyAndUploadCartoonFilter(
    context: Context,
    style: CartoonFilter.FilterStyle = CartoonFilter.FilterStyle.CLASSIC_CARTOON,
    fileName: String? = null
): Result<String> {
    return suspendCancellableCoroutine { continuation ->
        CartoonFilter.getInstance(context).applyAndUploadCartoonFilter(
            this,
            style,
            fileName
        ) { result ->
            continuation.resume(result)
        }
    }
}